.env#
# .env
NEXT_PUBLIC_NOTION_OAUTH_CLIENT_ID=<client-id>
NOTION_OAUTH_CLIENT_SECRET=<client-secret>
REDIRECT_URL=<client-redirect-url>Auth Flows#
1. install Integration#
integration access page

- display the permissions of the integration
- select access pages
Result#
- Cancel the access
- Allow the access: with selected pages
- all pages
- some of the pages
- no pages
Situations should be handled#
1. user not access all pages#
2. back to redirect_uri#
the page handle the integration install result
$REDIRECT_URL?code=03ae371f-3e6d-4b11-9c93-e9013ae47795&status=
Notion will redirect to the redirect_uri with different url parameters for the different install results:
code&state: Allow the accesserror: Cancel the access
Situations should be handled#
1. The display for Access Denied#
2. The display for Access Granted#
3. Get Auth Token#
use Notion API to get access token with the
codeparameter
const clientId = NEXT_PUBLIC_NOTION_OAUTH_CLIENT_ID;
const clientSecret = NOTION_OAUTH_CLIENT_SECRET;
const redirectUri = REDIRECT_URL;
const codeToToken = async (code: string) => {
const encoded = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
const response = await fetch("https://api.notion.com/v1/oauth/token", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Basic ${encoded}`,
},
body: JSON.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: redirectUri,
}),
}).then((res) => res.json());
const token = response?.access_token;
return token;
};Other Flows#
- List Pages#
- use Notion API and token to list pages
type Parent =
| {
type: "workspace";
workspace: true;
}
| {
type: "database_id";
database_id: string;
}
| {
type: "page_id";
page_id: string;
}
| {
type: "block_id";
block_id: string;
};
type Page = {
id: string;
parent: Parent;
title: string;
};
export const getAllPages = async ({
token,
search = "",
}: {
token: string;
search?: string;
}) => {
if (!token) throw new Error("token is required");
const response = await notionFetch("https://api.notion.com/v1/search", {
method: "POST",
token: `Bearer ${token}`,
body: {
query: search,
filter: {
property: "object",
value: "page",
},
},
});
const pages: Page[] = (response?.results ?? []).map((result: any) => ({
id: result?.id,
parent: result.parent as Parent,
title: result?.properties?.title?.title?.[0]?.plain_text,
}));
return pages;
};the Parent Object Document: https://developers.notion.com/reference/parent-object
Tips: the workspace is the top level scope of Notion.
Result Structure#
[
{
"id": "107a42ed-c8f7-80d3-bbd1-deb70d057400",
"parent": {
"type": "workspace",
"workspace": true
},
"title": "Job Application Tracker"
},
{
"id": "107a42ed-c8f7-80e0-9f0f-ff3ddf9cb92d",
"parent": {
"type": "workspace",
"workspace": true
},
"title": "Parent Page"
},
{
"id": "0e68c737-dabc-474f-add3-de1a473e7eb3",
"parent": {
"type": "page_id",
"page_id": "107a42ed-c8f7-80e0-9f0f-ff3ddf9cb92d"
},
"title": "Child Page"
}
]- Create New Page#
POST 'https://api.notion.com/v1/pages'body.parent.page_idorbody.parent.database_idis required
- Create New Database#
POST 'https://api.notion.com/v1/databases'body.parent.page_idis required
Situations should be handled#
1. For Creating new Page or Database, both require the parent.page_id, so we have to ask user to provide at least one page for our Integration.#
- Append Content to Page#
POST 'https://api.notion.com/v1/blocks/${pageId}/children'body:
{
"children": [
// ...
]
}Cases#
1. The redirect page - Error display#
display the error in redirect page after the notion install Integration page
2. The redirect page - Success and Waiting display#
time for us to using the url parameter code exchange the notion user token.
And we should store the token into user DB
3. Require available page - There is no available page as the Root Page#
Since there could be no pages we can access, or user select some pages which NOT follow our rule(like page title should be xxxx).
And Notion requires the parent.page_id when create new pages. So we have to ask user to provide at least one available page for our Integration.
4. The SaveNotion Component at right_top of message box.#
4.1 Check the Notion is ready for user to save#
- No token -> jump to Notion Integration install page
- No available page -> jump to
3. Require available page
4.2 Save Notion#
How to store the content? things are better organized in Notion#
- Append the content into the Root Page directly
- Create Page (per conversation) under the Root Page. (Like case1 in demo video)
- 1.Create a Database under the Root Page 2. Cerate new page under the Database (Like case2 in demo video)
How to optimize the user experience?#
- remember the last save rule
- there will be
${input}(new page)at the top of the list when there is no exact same title page. user click it we will create the new page and store the content in it.